package com.example.demo.service.impl;

import com.example.demo.domain.security.Token;
import com.example.demo.entity.AdminPermission;
import com.example.demo.entity.AdminRole;
import com.example.demo.entity.AdminUser;
import com.example.demo.entity.AdminUserLog;
import com.example.demo.enums.AdminUserLogActionEnum;
import com.example.demo.exception.UnknownException;
import com.example.demo.exception.UsernameExistException;
import com.example.demo.mapper.AdminUserMapper;
import com.example.demo.mapper.AdminUserRoleMapper;
import com.example.demo.security.userdetails.AdminUserManager;
import com.example.demo.security.userdetails.ManagerUserDetails;
import com.example.demo.service.AdminUserLogService;
import com.example.demo.service.AdminUserService;
import com.example.demo.unitls.TokenUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 管理员用户服务层实现类
 *
 * @author 叮当猫的百宝箱
 * @since 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AdminUserServiceImpl implements AdminUserService {

    private final AdminUserMapper adminUserMapper;

    private final AdminUserRoleMapper adminUserRoleMapper;

    private final PasswordEncoder passwordEncoder;

    private final AdminUserManager adminUserManager;

    private final TokenUtils tokenUtils;

    private final AdminUserLogService adminUserLogService;

    /**
     * 获取全部的管理员用户信息
     *
     * @return 管理员用户信息列表
     * @deprecated
     */
    @Override
    public List<AdminUser> getAllAdminUser() {
        return adminUserMapper.findAll();
    }

    /**
     * 根据管理员ID获取管理员信息
     *
     * @param adminUserId 管理员用户ID
     * @return 管理员用户信息
     */
    @Override
    public AdminUser getAdminUserById(Long adminUserId) {
        return adminUserMapper.findById(adminUserId);
    }

    /**
     * 根据管理员用户名获取管理员信息
     *
     * @param username 管理员用户名
     * @return 管理员用户信息
     */
    @Override
    public AdminUser getAdminUserByUsername(String username) {
        return adminUserMapper.findByUsername(username);
    }

    /**
     * 根据管理员ID获取管理员拥有哪些角色
     *
     * @param adminUserId 管理员用户ID
     * @return 管理员拥有的角色列表
     */
    @Override
    public List<AdminRole> getAdminRolesById(Long adminUserId) {
        return adminUserMapper.findRolesById(adminUserId);
    }

    /**
     * 根据管理员ID获取管理员拥有哪些权限
     *
     * @param adminUserId 管理员用户ID
     * @return 管理员拥有的角色列表(去重)
     */
    @Override
    public List<AdminPermission> getAdminPermissionsById(Long adminUserId) {
        return adminUserMapper.findPermissionsById(adminUserId);
    }

    /**
     * 根据管理员ID重置管理员密码
     *
     * @param adminUserid 管理员用户ID
     * @param password    管理员密码
     */
    @Override
    public void resetPasswordById(Long adminUserid, String password) {
        // TODO nothing
    }

    /**
     * 根据管理员用户ID修改密码
     *
     * @param adminUserId      管理员用户ID
     * @param originalPassword 原始密码
     * @param newPassword      新密码
     */
    @Override
    public void updatePasswordById(Long adminUserId, String originalPassword, String newPassword) {
        // TODO nothing
    }

    /**
     * 更新管理员角色信息
     *
     * @param adminUserId  管理员用户Id
     * @param adminRoleIds 管理员角色ID列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAdminUserRoles(Long adminUserId, List<Long> adminRoleIds) {
        // 查询当前管理员用户已拥有有的角色列表
        List<AdminRole> existingRoles = adminUserMapper.findRolesById(adminUserId);

        // 查询当前管理员用户已拥有有的角色ID集合
        List<Long> existingRoleIds = existingRoles.stream().map(AdminRole::getId).collect(Collectors.toList());

        // 找出需要添加的角色列表
        List<Long> rolesToAdd = new ArrayList<>(adminRoleIds);
        rolesToAdd.removeAll(existingRoleIds);

        // 找出需要删除的角色列表
        List<Long> rolesToRemove = new ArrayList<>(existingRoleIds);
        rolesToRemove.removeAll(adminRoleIds);

        log.error("existingRoleIds:{}", existingRoleIds);
        log.error("rolesToAdd:{}", rolesToAdd);
        log.error("rolesToRemove:{}", rolesToRemove);

        // 添加新角色
        adminUserRoleMapper.addAdminUserRoles(adminUserId, rolesToAdd);
        // 删除不再拥有的角色
        adminUserRoleMapper.deleteAdminUserRoles(adminUserId, rolesToRemove);
    }


    /**
     * 添加一个管理员用户
     *
     * @param adminUser 带添加的管理员用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createAdminUser(AdminUser adminUser) {
        createAdminUser(adminUser, null);
    }

    /**
     * 添加一个管理员用户
     *
     * @param adminUser 待添加的管理员用户
     * @param ipAddress 操作时的IP地址
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createAdminUser(AdminUser adminUser, String ipAddress) {
        // username 做为登陆的唯一标识是不能重复的
        AdminUser existingAdminUser = adminUserMapper.findByUsername(adminUser.getUsername());
        if (existingAdminUser != null) {
            throw new UsernameExistException("用户名已存在");
        }

        LocalDateTime now = LocalDateTime.now();
        AdminUser newAdminUser = new AdminUser();
        BeanUtils.copyProperties(adminUser, newAdminUser);

        // 加密一下密码
        newAdminUser.setPassword(passwordEncoder.encode(newAdminUser.getPassword()));

        newAdminUser.setCreateTime(now);
        newAdminUser.setUpdateTime(now);

        adminUserMapper.insert(newAdminUser);
        recordCreateAdminUserLog(adminUser, ipAddress);
    }


    /**
     * 通过管理员用户名和密码得到Token
     *
     * @param username 管理员用户名
     * @param password 管理员密码
     * @return 授权Token字符串
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Token getToken(String username, String password) {
        return getToken(username, password, null);
    }

    /**
     * 通过管理员用户名和密码得到Token
     *
     * @param username  管理员用户名
     * @param password  管理员密码
     * @param ipAddress 登陆时的IP
     * @return 授权Token字符串
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Token getToken(String username, String password, String ipAddress) {
        UserDetails userDetails = adminUserManager.loadUserByUsername(username);
        if (userDetails == null) {
            throw new UsernameExistException("用户名不存在");
        }

        if (!passwordEncoder.matches(password, userDetails.getPassword())) {
            throw new UsernameExistException("用户名或者密码不正确");
        }

        Token token = tokenUtils.generateToken(userDetails);
        // 记录日志
        recordLoginLog((ManagerUserDetails) userDetails, ipAddress, token.getToken());

        return token;
    }

    /**
     * 管理管理员用户ID 删除管理员用户信息
     *
     * @param adminUserId 管理员用户ID
     */
    @Override
    public void deleteAdminUserById(Long adminUserId) {
        adminUserMapper.deleteById(adminUserId);
    }

    /**
     * 刷新token
     *
     * <pre>
     * 需要刷新token的情况如下：
     * 1. 当原token信息将要过期时, 需要刷新token防止客户端操作时候突然退出登陆
     * 2. 由于用户权限直接保存在token中,当用户的权限或者角色发生变动时需要刷新token，这样就不用重新登陆更新权限了
     * </pre>
     *
     * @return 新的token字符串
     */
    @Override
    public Token refreshToken() {

        // 从security 上下文中获取到当前用户
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (!(authentication.getPrincipal() instanceof ManagerUserDetails)) {
            throw new UnknownException("错误的用户类型");
        }

        UserDetails userDetails = (ManagerUserDetails) authentication.getPrincipal();

        // 重新查询数据库， 这里会重新从数据库中获取最新的权限信息
        userDetails = adminUserManager.loadUserByUsername(userDetails.getUsername());

        // TODO 当刷新了TOKEN之后要将原来的Token信息设置为失效

        return tokenUtils.generateToken(userDetails);
    }

    /**
     * 记录登陆日志
     *
     * @param ipAddress ip地址
     * @param token     token字符串
     */
    private void recordLoginLog(ManagerUserDetails userDetails, String ipAddress, String token) {
        AdminUserLog adminUserLog = new AdminUserLog();
        adminUserLog.setAdminUserId(userDetails.getId());
        adminUserLog.setAction(AdminUserLogActionEnum.LOGIN.getValue());
        adminUserLog.setIpAddress(ipAddress);
        adminUserLog.setDetails(token);
        adminUserLogService.create(adminUserLog);
    }

    /**
     * 记录添加管理员用户日志
     *
     * @param adminUser 新增加的管理员用户
     * @param ipAddress 添加用户的IP地址
     */
    private void recordCreateAdminUserLog(AdminUser adminUser, String ipAddress) {
        UserDetails userDetails = tokenUtils.getUserDetailsByContextHolder();
        if (userDetails instanceof ManagerUserDetails) {
            ManagerUserDetails managerUserDetails = (ManagerUserDetails) userDetails;
            Long id = managerUserDetails.getId();
            AdminUserLog adminUserLog = new AdminUserLog();
            adminUserLog.setAdminUserId(id);
            adminUserLog.setIpAddress(ipAddress);
            adminUserLog.setAction(AdminUserLogActionEnum.CREATE_ADMIN_USER.getValue());
            adminUserLog.setDetails(adminUser.getUsername());
            adminUserLogService.create(adminUserLog);
        }
    }


}
